package encrypt

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"log"
	"os"
)

/**
 * @Description: 生成RSA公钥和私钥
 * @param bits: 生成的密钥长度
 * @param outPath: 密钥文件输出路径
 */
func RSAGenerate(bits int, outPath string) {

	//----------------------------------------------private key

	// GenerateKey generates an RSA keypair of the given bit size using the
	// random source random (for example, crypto/rand.Reader).
	privateKey, err := rsa.GenerateKey(rand.Reader, bits)
	if err != nil {
		log.Fatal(err)
	}

	//serialize privatekey to ASN.1 der by x509.MarshalPKCS1PrivateKey
	x509privatekey := x509.MarshalPKCS1PrivateKey(privateKey)

	//encode x509 to pem and save to file
	//1. create privatefile
	privatekeyfile, err := os.Create(outPath + "/privatekey.pem")
	if err != nil {
		log.Fatal(err)
	}
	defer privatekeyfile.Close()
	//2. new a pem block struct object
	privatekeyblock := pem.Block{
		Type:    "RSA Private Key",
		Headers: nil,
		Bytes:   x509privatekey,
	}
	//3. save to file
	pem.Encode(privatekeyfile, &privatekeyblock)

	//----------------------------------------------public key

	//get public key
	publickey := privateKey.PublicKey
	//serialize publickey to ASN.1 der by x509.MarshalPKCS1PublicKey
	x509publickey, _ := x509.MarshalPKIXPublicKey(&publickey)

	//encode x509 to pem and save to file
	//1. create publickeyfile
	publickeyfile, err := os.Create(outPath + "/publickey.pem")
	if err != nil {
		log.Fatal(err)
	}
	defer publickeyfile.Close()

	//2. new a pem block struct object
	publickeyblock := pem.Block{
		Type:    "RSA Public Key",
		Headers: nil,
		Bytes:   x509publickey,
	}

	//3. save to file
	pem.Encode(publickeyfile, &publickeyblock)
}

/**
 * @Description: 通过公钥加密
 * @param plainText: 明文
 * @param publickeypath: 公钥文件路径
 */
func RsaEncrypt(plainText []byte, publickeypath string) []byte {
	//open publickeyfile
	publickeyfile, err := os.Open(publickeypath)
	if err != nil {
		log.Fatal(err)
	}
	defer publickeyfile.Close()

	//get publickeyfile info
	publickeyfileInfo, _ := publickeyfile.Stat()

	//read publickeyfile content
	//1. make size
	buf := make([]byte, publickeyfileInfo.Size())
	//2. read file to buf
	publickeyfile.Read(buf)
	//3. decode pem
	publickeyDecodeBlock, _ := pem.Decode(buf)
	//4. x509 decode
	publicKeyInterface, err := x509.ParsePKIXPublicKey(publickeyDecodeBlock.Bytes)
	if err != nil {
		log.Fatal(err)
	}
	//assert
	publicKey := publicKeyInterface.(*rsa.PublicKey)

	//encrypt plainText
	cipherText, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, plainText)
	if err != nil {
		log.Fatal(err)
	}

	return cipherText
}

func RsaDecrypt(cipherText []byte, privatekeypath string) []byte {

	//open privatekeyfile
	privatekeyfile, err := os.Open(privatekeypath)
	if err != nil {
		log.Fatal(err)
	}
	defer privatekeyfile.Close()
	//get privatekeyfile content
	privatekeyinfo, _ := privatekeyfile.Stat()
	buf := make([]byte, privatekeyinfo.Size())
	privatekeyfile.Read(buf)
	//pem decode
	privatekeyblock, _ := pem.Decode(buf)
	//X509 decode
	privateKey, err := x509.ParsePKCS1PrivateKey(privatekeyblock.Bytes)
	if err != nil {
		log.Fatal(err)
	}
	//decrypt the cipher
	plainText, _ := rsa.DecryptPKCS1v15(rand.Reader, privateKey, cipherText)

	return plainText
}
